﻿//
// Copyright (c) 2011 David Díaz & Fran Pedreira
//
// This file is part of PlanSim.
//
// PlanSim is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PlanSim is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with PlanSim. If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace PlanSim.Core
{
	/// <summary>
	/// Implementa una simulación.
	/// </summary>
	public class Simulacion
	{
		/// <summary>
		/// Configuración de la simulación.
		/// </summary>
		Configuracion configuracion;

		/// <summary>
		/// Planificador que ejecuta la simulación.
		/// </summary>
		public Planificador Planificador
		{
			get { return planificador; }
			//set { planificador = value; }
		}
		Planificador planificador;

		/// <summary>
		/// Procesos enviados al planificador.
		/// </summary>
		public List<IProceso> Procesos
		{
			get { return procesos; }
		}
		List<IProceso> procesos = new List<IProceso>();

		/// <summary>
		/// Indica el máximo ciclo de simulación que se ha llegado a ejecutar.
		/// </summary>
		public int MaximoCiclos
		{
			get { return maximoCiclos; }
			// set { maximoCiclos = value; }
		}
		int maximoCiclos = 0;

		public Simulacion(Configuracion configuracion)
		{
			this.configuracion = configuracion;

			planificador = new Planificador(configuracion);

			planificador.ComienzaCiclo += new EventHandler(planificador_ComienzaCiclo);
			planificador.FinalizaCiclo += new EventHandler(planificador_FinalizaCiclo);
		}

		public void IniciarSimulacion(int frecuencia)
		{
			planificador.IniciarSimulacion(frecuencia);
		}

		public void PausarSimulacion()
		{
			planificador.PausarSimulacion();
		}

        public void DetenerSimulacion()
        {
            planificador.DetenerSimulacion();
        }

		/// <summary>
		/// Lógica de inserción de procesos
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void planificador_ComienzaCiclo(object sender, EventArgs e)
		{
			CicloEventArgs args = (CicloEventArgs)e;

			foreach (Configuracion.Proceso p in configuracion.Procesos)
			{
				if (p.Entrada == args.Ciclo)
				{
					int periodoES = 0;
					int duracionES = 0;

					for (int i = 0; i < p.AccesosADispositivos.Count; ++i)
						if (p.AccesosADispositivos[i].Ciclico)
						{
							periodoES = p.AccesosADispositivos[i].Periodo;
							duracionES = p.AccesosADispositivos[i].Duracion;

							// Me quedo con el primero que especifique ejecución cíclica ya que solo disponemos
							// de un dispositivo.
							break;
						}

					IProceso proceso = new Proceso(p.Nombre, p.Entrada, p.Duracion, periodoES, duracionES, Color.FromArgb(p.Color));
					procesos.Add(proceso);
					planificador.InsertarProceso(proceso, p.ColaInicial);
				}
			}
		}

		/// <summary>
		/// Actualización de información.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		/// <remarks>
		/// ¡Cuidadín! el ciclo que finaliza hay que sacarlo de ((Planificador.CicloEventArgs)e).Ciclo.
		/// No se puede asegurar que planificador.CicloActual vaya a valer lo mismo o se haya incrementado ya.
		/// </remarks>
		private void planificador_FinalizaCiclo(object sender, EventArgs e)
		{
			CicloEventArgs args = (CicloEventArgs)e;

			// Actualiza el numero de ciclos de la simulación.
			maximoCiclos = Math.Max(maximoCiclos, args.Ciclo);

			foreach (Proceso proceso in procesos)
			{
				EstadoProceso.PosicionProceso posicion = planificador.PosicionProceso(proceso);

				if (posicion == EstadoProceso.PosicionProceso.ColaPreparados)
					++proceso.CiclosEsperaCPU;
				else if (posicion == EstadoProceso.PosicionProceso.ColaES)
					++proceso.CiclosEsperaES;

				proceso.Historia[args.Ciclo] = new EstadoProceso(posicion, proceso.Pendiente);
			}
		}
	}
}
